#include "NetListModel.h"

#include "LeIpc2581/enumtranslator.h"
#include "LeIpc2581/LogicalNet.h"
#include "LeIpc2581/PhyNet.h"

#include <QDebug>
#include <QHash>
#include <QSet>

struct NetItem
{
    NetItem():
        logicalNet(nullptr), physicalNet(nullptr)
    {}

    explicit NetItem(const Ipc2581b::LogicalNet *logicalNet,
                     const Ipc2581b::PhyNet *physicalNet):
        logicalNet(logicalNet), physicalNet(physicalNet)
    {}

    const Ipc2581b::LogicalNet *logicalNet;
    const Ipc2581b::PhyNet *physicalNet;
    QSet<QString> components;
};

class NetListModelPrivate
{
    Q_DISABLE_COPY(NetListModelPrivate)
    Q_DECLARE_PUBLIC(NetListModel)
    NetListModel * const q_ptr;

    explicit NetListModelPrivate(NetListModel *model):
        q_ptr(model)
    {}

    QMap<QString, NetItem> m_data;

    inline void clear()
    {
        m_data.clear();
    }
};

NetListModel::NetListModel(QObject *parent):
    QAbstractTableModel(parent),
    d_ptr(new NetListModelPrivate(this))
{
}

NetListModel::~NetListModel()
{

}

void NetListModel::addLogicalNet(const Ipc2581b::LogicalNet *net)
{
    Q_D(NetListModel);
    NetItem item = d->m_data[net->name];
    if (item.logicalNet != nullptr)
        qWarning() << QString("NetListModel::addLogicalNet: Overriding logical net '%1'").arg(net->name);
    item.logicalNet = net;
    for (const Ipc2581b::PinRef *pinRef: net->pinRefList)
        if (pinRef->componentRefOptional.hasValue)
            item.components.insert(pinRef->componentRefOptional.value);
    d->m_data[net->name] = item;
}

void NetListModel::addPhysicalNet(const Ipc2581b::PhyNet *net)
{
    Q_D(NetListModel);
    NetItem item = d->m_data[net->name];
    if (item.physicalNet != nullptr)
        qWarning() << QString("NetListModel::addPhysicalNet: Overriding physical net '%1'").arg(net->name);
    item.physicalNet = net;
    d->m_data[net->name] = item;
}

void NetListModel::clear()
{
    Q_D(NetListModel);

    beginResetModel();
    d->clear();
    endResetModel();
}

QVariant NetListModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if (orientation != Qt::Horizontal)
        return QVariant();

    if (role != Qt::DisplayRole)
        return QVariant();

    switch (section)
    {
        case NameColumn:
            return "Name";
        case NetClassColumn:
            return "Class";
        case PinCountColumn:
            return "Pin count";
        case ComponentCountColumn:
            return "Component count";
        case NodeCountColumn:
            return "Node count";
        default:
            return QVariant();
    }
}

int NetListModel::rowCount(const QModelIndex &parent) const
{
    Q_D(const NetListModel);

    if (parent.isValid())
        return 0;

    return d->m_data.count();
}

int NetListModel::columnCount(const QModelIndex &parent) const
{
    if (parent.isValid())
        return 0;

    return ColumnCount;
}

QVariant NetListModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())
        return QVariant();

    if (index.row() < 0 || index.row() >= rowCount())
        return QVariant();

    Q_D(const NetListModel);
    const QString name = d->m_data.keys().at(index.row());
    const NetItem item = d->m_data.value(name);

    switch (role)
    {
        case Qt::DisplayRole:
            switch (index.column())
            {
                case NameColumn:
                    return item.logicalNet->name;
                case NetClassColumn:
                    if (item.logicalNet->netClassOptional.hasValue)
                        return Ipc2581b::EnumTranslator::netClassText(item.logicalNet->netClassOptional.value);
                    return QVariant();
                case PinCountColumn:
                    return item.logicalNet->pinRefList.count();
                case ComponentCountColumn:
                    return item.components.count();
                case NodeCountColumn:
                    if (item.physicalNet != nullptr)
                        return item.physicalNet->phyNetPointList.count();
                    return QVariant();
                default:
                    return QVariant();
            }
        case Qt::EditRole:
            switch (index.column())
            {
                case NameColumn:
                    return item.logicalNet->name;
                case NetClassColumn:
                    if (item.logicalNet->netClassOptional.hasValue)
                        return QVariant::fromValue<Ipc2581b::NetClass>(item.logicalNet->netClassOptional.value);
                    return QVariant();
                case PinCountColumn:
                    return item.logicalNet->pinRefList.count();
                case ComponentCountColumn:
                    return item.components.count();
                case NodeCountColumn:
                    if (item.physicalNet != nullptr)
                        return item.physicalNet->phyNetPointList.count();
                    return QVariant();
                default:
                    return QVariant();
            }
        default:
            return QVariant();
    }
}
